package com.iflytek.jzcpx.procuracy.ocr.service.impl;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.iflytek.jzcpx.procuracy.common.util.JSONUtil;
import com.iflytek.jzcpx.procuracy.ocr.common.enums.RecognizeTaskStatusEnum;
import com.iflytek.jzcpx.procuracy.ocr.dao.RecognizeTaskDao;
import com.iflytek.jzcpx.procuracy.ocr.entity.RecognizeTask;
import com.iflytek.jzcpx.procuracy.ocr.service.RecognizeFileService;
import com.iflytek.jzcpx.procuracy.ocr.service.RecognizeTaskService;
import com.iflytek.jzcpx.procuracy.tools.common.PropUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;

/**
 * @author <a href=mailto:ktyi@iflytek.com>伊开堂</a>
 * @date 2019-08-12 19:30
 */
@Service
public class RecognizeTaskServiceImpl extends ServiceImpl<RecognizeTaskDao, RecognizeTask>
        implements RecognizeTaskService {
    private static final Logger logger = LoggerFactory.getLogger(RecognizeTaskServiceImpl.class);

    @Autowired
    private RecognizeFileService recognizeFileService;

    @Autowired
    private RestTemplate restTemplate;

    @Value("${iflytek.zhongtai.service.mq.url}")
    private String zhongtaiServerUrl;

    @Value("${swx.systemid}")
    private String systemid;

    @Value("${swx.dzjz_token}")
    private String token;

    @Override
    public Long create(String systemid, String dwbm, String bsbh, String taskid,String bmsah) {
        RecognizeTask task = new RecognizeTask();
        Date now = new Date();
        task.setCreateTime(now);
        task.setUpdateTime(now);
        task.setSystemid(systemid);
        task.setDwbm(dwbm);
        task.setBsbh(bsbh);
        task.setTaskid(taskid);
        task.setStatus(RecognizeTaskStatusEnum.INIT.toString());
        task.setBmsah(bmsah);
        boolean saveSuccess = save(task);
        logger.info("保存图像识别批量任务, systemid[ {} ], dwbm[ {} ], bsbh[ {} ], taskid[ {} ]", systemid, dwbm, bsbh, taskid);
        return saveSuccess ? task.getId() : null;
    }

    @Override
    public boolean abnormalEnd(Long taskId, String errorInfo) {
        RecognizeTask params = new RecognizeTask();
        Date now = new Date();
        params.setUpdateTime(now);
        params.setId(taskId);
        params.setStatus(RecognizeTaskStatusEnum.END.toString());
        params.setRemark(errorInfo);
        logger.info("图像识别批量任务异常结束, taskId[ {} ], errorInfo[ {} ]", taskId, errorInfo);
        return updateById(params);
    }

    /**
     * 更新图像识别任务状态
     *
     * @param taskId           任务 id
     * @param targetStatusEnum 要更新成的状态
     * @return
     */
    @Override
    public boolean updateStatus(Long taskId, RecognizeTaskStatusEnum targetStatusEnum) {
        RecognizeTask params = new RecognizeTask();
        Date now = new Date();
        params.setUpdateTime(now);
        params.setId(taskId);
        params.setStatus(targetStatusEnum.toString());
        logger.info("更新图像识别任务状态, taskId[ {} ], targetStatusEnum[ {} ]", taskId, targetStatusEnum);
        return updateById(params);
    }

    @Override
    public List<Long> findPushableRecognizeTaskId() {
        Integer gapDays = PropUtils.getInt("pushable_recog_task_days", 365);
        Date startTime = DateUtils.addDays(new Date(), -1 * gapDays);

        // 部门受案号不为空, 状态为处理中, 所有子任务全部完成
        LambdaQueryWrapper<RecognizeTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(RecognizeTask::getId).eq(RecognizeTask::getStatus,
                                                     RecognizeTaskStatusEnum.PROCESSING.name()).isNotNull(
                RecognizeTask::getBmsah).gt(RecognizeTask::getCreateTime, startTime);
        List<RecognizeTask> recognizeTaskList = list(queryWrapper);
        if (CollectionUtils.isEmpty(recognizeTaskList)) {
            return null;
        }

        // 查询出有子任务未完成的 taskId
        List<Long> recognizeTaskIdList = recognizeTaskList.stream().map(RecognizeTask::getId).collect(Collectors.toList());
        Map<Long, List<Long>> undoneFileIdMap = recognizeFileService.findUndoneFileIdMap(recognizeTaskIdList);
        if (MapUtils.isNotEmpty(undoneFileIdMap)) {
            // 移除未完成的, 剩下的任务就是已完成的, 可以推送结果了
            recognizeTaskIdList.removeAll(undoneFileIdMap.keySet());
        }

        if (CollectionUtils.isEmpty(recognizeTaskIdList)) {
            return null;
        }

        logger.info("更新图像识别任务状态为[提交中], idList: {}", JSONUtil.toStrDefault(recognizeTaskIdList));
        LambdaUpdateWrapper<RecognizeTask> wrapper = new LambdaUpdateWrapper<>();
        // 达梦数据库只支持2048个入参
        int size = recognizeTaskIdList.size();
        int step = 2000;
        int start, end = 0;
        for (int i = 0; i < Math.ceil(size / (float) step); i++) {
            start = i * step;
            end = Math.min(size, start + step);
            List<Long> subList = recognizeTaskIdList.subList(start, end);

            wrapper.in(RecognizeTask::getId, subList).set(RecognizeTask::getStatus,
                                                          RecognizeTaskStatusEnum.SUBMITTING.name());
            update(wrapper);
        }

        return recognizeTaskIdList;
    }

    @Override
    public boolean pushSuccessMessage(RecognizeTask recognizeTask) {
        //RequestEntity<String>
        long start = System.currentTimeMillis();
        String result = "";
        try {
            JSONObject object = new JSONObject();
            object.put("bsbh", recognizeTask.getBsbh());
            object.put("systemid",systemid);

            String url = zhongtaiServerUrl;

            HttpHeaders headers = new HttpHeaders();
            headers.add("Accept", "*/*");
            headers.add("systemid", systemid);
            headers.add("dzjz_token", token);

            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<String> formEntity = new HttpEntity<String>(object.toString(), headers);

            logger.info("图像识别结果推送, url: {}, body: {}", url, object);
            result = restTemplate.postForObject(url, formEntity, String.class);
        } catch (Exception ex) {
            logger.warn("图像识别结果推送异常", ex);

            if (PropUtils.getBool("swx_request_debug", false)) {
                logger.info(">>>>> 调试 <<<<<<");
                try {
                    result = FileUtils.readFileToString(
                            new File(PropUtils.getProp("swx_request_debug_path", "/") + "/getTxStatus.json"));
                }
                catch (IOException e) {
                    //
                }
            }
            else {
                return false;
            }
        } finally {
            logger.info("图像识别结果推送结束, 响应: {}, 耗时: {}ms", result, System.currentTimeMillis() - start);
        }
        return true;
    }

    /**
     * 更新失败次数
     *
     * @param recognizeTaskId 任务 id
     * @return 更新成功与否
     */
    @Override
    public boolean updateTimes(Long recognizeTaskId) {
	if (null == recognizeTaskId) {
		return false;
	}
	RecognizeTask recognizeTask = getById(recognizeTaskId);
	if (null == recognizeTask) {
		logger.error("根据recognizeTaskId查询记录不存在 recognizeTaskId:{}", recognizeTaskId);
		return false;
	}
    logger.info("推送失败次数 {}", recognizeTask.getFailNums());
	RecognizeTask params = new RecognizeTask();
	params.setId(recognizeTaskId);
	params.setUpdateTime(new Date());
    Integer failNums = recognizeTask.getFailNums();
    params.setFailNums(failNums == null ? 0 : failNums  + 1);
	logger.info("recognize 推送失败次数更新, recognizeTaskId[ {} ], params: {}", recognizeTaskId, params.getFailNums());
	return updateById(params);
    }

    @Override
    public int cleanData(Date startDatetime, Date endDatetime) {
        logger.info("清理recognizeTask数据, 起止时间: {} - {}", startDatetime, endDatetime);
        Assert.isTrue(startDatetime != null || endDatetime != null, "开始和结束时间不能同时为空");

        int cleanCount = 0;

        List<RecognizeTask> recognizeTasks = null;
        int pageSize = 100;
        do {
            Page<RecognizeTask> page = new Page<>(1, pageSize);
            LambdaQueryWrapper<RecognizeTask> wrapper = new LambdaQueryWrapper<>();
            if (startDatetime != null) {
                wrapper.gt(RecognizeTask::getCreateTime, startDatetime);
            }
            if (endDatetime != null) {
                wrapper.lt(RecognizeTask::getCreateTime, endDatetime);
            }
            IPage<RecognizeTask> recognizeTaskPage = this.baseMapper.selectPage(page, wrapper);
            if (recognizeTaskPage == null || CollectionUtils.isEmpty(recognizeTaskPage.getRecords())) {
                logger.info("没有待清理的recognizeTask数据了");
                return cleanCount;
            }

            recognizeTasks = recognizeTaskPage.getRecords();
            logger.debug("查询到待清理的recognizeTask数据, size: {}, page: {}", recognizeTasks.size(), page);

            for (RecognizeTask recognizeTask : recognizeTasks) {
                logger.info("删除recognizeTask数据, recognizeTask: {}", recognizeTask);
                if (removeById(recognizeTask)) {
                    cleanCount++;
                }
            }
        } while (CollectionUtils.size(recognizeTasks) >= pageSize);

        return cleanCount;
    }

    @Override
    public List<RecognizeTask> listByBsbh(String bsbh) {
        if (StringUtils.isBlank(bsbh)) {
            return new ArrayList<>();
        }
        QueryWrapper<RecognizeTask> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(RecognizeTask::getBsbh, bsbh);
        return list(queryWrapper);
    }
}
